﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<title>Rules Tests</title>
		
		<link href="../Common/Styles/qunit.css" type="text/css" rel="stylesheet" />
		
		<script src="../Common/Scripts/JQuery/jquery-1.3.2.js" type="text/javascript"></script>
		<script src="../Common/Scripts/QUnit/qunit.js" type="text/javascript"></script>
		<script src="../Common/Scripts/Microsoft/MicrosoftAjax.js" type="text/javascript"></script>
		<script src="../Common/Scripts/ExoWeb/exoweb.js" type="text/javascript"></script>
		<script src="../Common/Scripts/QUnit/qunit.ext.js" type="text/javascript"></script>
		<script src="../Common/Scripts/ExoWeb/exoweb.model.js" type="text/javascript"></script>
		<script src="../Common/Scripts/ExoWeb/exoweb.mapper.js" type="text/javascript"></script>
		<script src="../Common/Scripts/ExoWeb/exoweb.mock.js" type="text/javascript"></script>
		<script src="../Common/Scripts/mock-driver.js" type="text/javascript"></script>
		
		<script type="text/javascript">
			var context = ExoWeb.context({ model: { driver: { id: "1", from: "Driver" } } });

			setupTest("allowed-values-basic", { description: "Basic Allowed Values Tests", expect: 2 }, function() {
				var rule = Driver.$Cars.rule(ExoWeb.Model.Rule.allowedValues);
				ok(rule, "Rule can be retrieved via Property.rule(type)");
				same(context.model.driver.get_Dealer().get_AvailableCars(), rule.values(context.model.driver),
					"Allowed values rule \"values\" function should return the referenced property value.");
			});

			setupTest("compare-number-gte", { description: "Compare Rule (number greater than or equal to)", expect: 4 }, function () {
				var rule = Driver.$MilesDriven.rule(ExoWeb.Model.Rule.compare);

				equals(rule.err.get_message(), "Miles Driven must be greater than equal to Miles Driven Quota", "Check message text");

				context.model.driver.set_MilesDrivenQuota(100000);
				ok(rule.satisfies(context.model.driver), "Comparison should be valid: equal");

				context.model.driver.set_MilesDrivenQuota(1000);
				ok(rule.satisfies(context.model.driver), "Comparison should be valid: greater than");

				context.model.driver.set_MilesDrivenQuota(10000000);
				ok(!rule.satisfies(context.model.driver), "Comparison should NOT be valid: less than");
			});

			setupTest("compare-conditions", { description: "Compare Rule Attached Conditions", expect: 6 }, function () {
				var rule = Driver.$DateCreated.rule(ExoWeb.Model.Rule.compare);
				ok(rule, "Rule can be retrieved via Property.rule(type)");

				equals(context.model.driver.meta.conditions(Driver.$DateCreated).length, 0, "No condition attached for rule");

				context.model.driver.set_DateCreated(new Date(1950, 1, 1));
				equals(context.model.driver.meta.conditions(Driver.$DateCreated).length, 1, "Condition attached for rule");

				context.model.driver.set_BirthDate(new Date(1949, 1, 1));
				equals(context.model.driver.meta.conditions(Driver.$DateCreated).length, 0, "Condition attached for rule");

				context.model.driver.set_BirthDate(null);
				equals(context.model.driver.meta.conditions(Driver.$DateCreated).length, 0, "Condition should not be attached for null comparison value");

				context.model.driver.set_BirthDate(new Date());
				context.model.driver.set_DateCreated(null);
				equals(context.model.driver.meta.conditions(Driver.$DateCreated).length, 0, "Condition should not be attached for null source value");
			});

			setupTest("requiredIf-list", { description: "RequiredIf Rule (list)", expect: 6 }, function () {
				var rule = Driver.$PrimaryCar.rule(ExoWeb.Model.Rule.requiredIf);

				context.model.driver.set_PrimaryCar(null);
				ok(rule.required(context.model.driver), "Primary Car is required: Cars is non-empty");
				ok(!rule.satisfies(context.model.driver), "Rule is NOT satisfied: Primary Car does not have a value");

				// Make a copy of the driver's car array.
				var cars = context.model.driver.get_Cars().map(function(c) { return c; });
				Array.clear(context.model.driver.get_Cars());
				ok(!rule.required(context.model.driver), "Primary Car is NOT required: Cars is empty");
				ok(rule.satisfies(context.model.driver), "Rule is satisfied: not required");

				Array.addRange(context.model.driver.get_Cars(), cars);
				context.model.driver.set_PrimaryCar(cars[0]);
				ok(rule.required(context.model.driver), "Primary Car is required: Cars is non-empty");
				ok(rule.satisfies(context.model.driver), "Rule is satisfied: Primary Car has a value");
			});

			setupTest("requiredIf-number-gt", { description: "RequiredIf Rule (number greater than)", expect: 7 }, function () {
				var rule = Driver.$RetirementGoalDate.rule(ExoWeb.Model.Rule.requiredIf);

				ok(!rule.required(context.model.driver), "Retirement Goal Date is NOT required: Miles Driven is less than 200,000");
				ok(rule.satisfies(context.model.driver), "Rule is satisfied: not required");

				context.model.driver.set_MilesDriven(200000);
				ok(!rule.required(context.model.driver), "Retirement Goal Date is NOT required: Miles Driven is equal to 200,000");

				context.model.driver.set_MilesDriven(250000);
				ok(rule.required(context.model.driver), "Retirement Goal Date is required: Miles Driven is greater than 200,000");
				ok(!rule.satisfies(context.model.driver), "Rule is NOT satisfied: Retirement Goal Date does not have a value");

				var goal = new Date();
				goal.setFullYear(goal.getFullYear() + 5);
				context.model.driver.set_RetirementGoalDate(goal);
				ok(rule.required(context.model.driver), "Retirement Goal Date is required: Miles Driven is greater than 200,000");
				ok(rule.satisfies(context.model.driver), "Rule is satisfied: Retirement Goal Date has a value");
			});

			setupTest("compare-logic", { description: "Compare Logic" }, function () {
				var ops = ["Equal", "NotEqual", "GreaterThan", "GreaterThanEqual", "LessThan", "LessThanEqual" ];

				function compare(val1, val2, expect) {
					for (var i = 0; i < expect.length; i++) {
						equals(ExoWeb.Model.Rule.compare.compare(val1, ops[i], val2),
							expect[i], val1 + " " + ops[i] + " " + val2);
					}
				}

				var undef;

				//                        ==		!=		>		>=		<		<=
				compare("abc", "abc",	[ true,		false,	false,	true,	false,	true ]);
				compare("abc", "def",	[ false,	true,	false,	false,	true,	true ]);
				compare("abc", "aaa",	[ false,	true,	true,	true,	false,	false ]);
				compare(0, 0,			[ true,		false,	false,	true,	false,	true ]);
				compare(0, 4,			[ false,	true,	false,	false,	true,	true ]);
				compare(21, 4,			[ false,	true,	true,	true,	false,	false ]);
				compare("abc", null,	[ false,	true,	undef,	undef,	undef,	undef ]);
				compare("abc", undef,	[ false,	true,	undef,	undef,	undef,	undef ]);
				compare([], null,		[ true,		false,	undef,	undef,	undef,	undef ]);
				compare(["a"], null,	[ false,	true,	undef,	undef,	undef,	undef ]);
			});

			var hasRunDriverDescription = false;
			function calculateDriverDescription() {
				hasRunDriverDescription = true;
				return $format("Driver \"{Name}\" owned by \"{Owner.Name}\".", this);
			}

			setupTest("rule-force-execution", { description: "Forcing Execution of a Rule's Properties", expect: 4 }, function() {
				equals(context.model.driver.get_Description(), calculateDriverDescription.call(context.model.driver), "Property should have been calculated");

				hasRunDriverDescription = false;
				context.model.driver.set_Name(context.model.driver.get_Name() + " Jr.");
				ok(hasRunDriverDescription, "Rule should run after related property changes");

				hasRunDriverDescription = false;
				Person.meta.executeRules(context.model.driver, Driver.$Description);
				ok(hasRunDriverDescription, "Rule should run after calling \"executeRules()\" on Driver.$Description");

				hasRunDriverDescription = false;
				Person.meta.executeRules(context.model.driver.get_Owner(), CarOwner.$Name);
				ok(!hasRunDriverDescription, "Rule should NOT run after calling \"executeRules()\" on CarOwner.$Name since it is not a target of the rule");
			});

			context.ready(function () {
				executeTest("allowed-values-basic");

				executeTest("compare-number-gte");
				executeTest("compare-conditions");
				executeTest("requiredIf-list");
				executeTest("requiredIf-number-gt");
				executeTest("compare-logic");

				Driver.meta.addProperty({ name: "Description", type: String })
					.calculated({ fn: calculateDriverDescription, basedOn: ["this.Owner.Name", "this.Name"] })
					._readySignal.waitForAll(executeTest.prependArguments("rule-force-execution"));
			});
		</script>
	</head>
	<body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView" xmlns:content="javascript:ExoWeb.UI.Content"
		xmlns:template="javascript:ExoWeb.UI.Template" sys:activate="*">
		
		<!-- QUnit Display -->
		<h1 id="qunit-header">Test Results:</h1>
		<h2 id="qunit-banner"></h2>
		<h2 id="qunit-userAgent"></h2>
		<ol id="qunit-tests"></ol>
	</body>
</html>
